package poker.entities;

import java.util.ArrayList;
import java.util.Random;

import poker.logic.AbstractHandConfiguration;
import poker.util.ConstantCardStrings;
import poker.util.MathUtils;



public class Hand {
	
	public final int MAX_CARDS = 7;
	private ArrayList<Card> currentCards;
	private AbstractHandConfiguration currentHandConf;
	
	public Hand(){
		currentCards = null;
	}
	
	public Hand(ArrayList<Card> cards){
		if(cards.size() <= MAX_CARDS){
			currentCards = cards;
		}
		else{
			currentCards = null;
		}
	}
	
	/**
	 * addCard
	 * @param card to add to the current hand.
	 */
	public void addCard(Card card){
		if(currentCards == null)currentCards = new ArrayList<Card>();
		currentCards.add(card);
	}
	
	/**
	 * removeCard
	 * @param card to remove from the current hand.
	 */
	public void removeCard(Card card){
		if(currentCards != null){
			for(int index = 0; index < currentCards.size(); index++){
				if(currentCards.get(index).isExactSame(card)){
					currentCards.remove(index);
				}
			}
		}
	}
	
	/***********************************************************
	 * HAND CARDS COMPARISON METHODS
	 ***********************************************************/
		
	/**
	 * 	getWeakest()
	 * @return Returns weakest card in current hand. Following the CLUBS/DIAMONDS/HEARTS/SPADES sorting.
	 */
	public Card getWeakest(){
		if(currentCards != null && !(currentCards.isEmpty()) && currentCards.size() > 0){
			Card dummyCard = currentCards.get(0);
			for(int index = 0; index < currentCards.size(); index++){
				if(dummyCard.getForce().retrieve() > currentCards.get(index).getForce().retrieve()){
					dummyCard = currentCards.get(index);
				}
			}
			return dummyCard;
		}
		return new Card();
	}
		
	/**
	 * 	getStrongest()
	 * @return Returns first strongest card in current hand.
	 */
	public Card getStrongest(){
		if(currentCards.size() == 1){
			return currentCards.get(0);
		}
		else if(currentCards != null && currentCards.size() > 0){
			Card dummyCard = currentCards.get(0);
			for(int index = 1; index < currentCards.size(); index++){
				if(dummyCard.getForce().retrieve() < currentCards.get(index).getForce().retrieve()){
					dummyCard = currentCards.get(index);
				}
			}
			return dummyCard;
		}
		return new Card();
	}
	
	/**
	 * 		getStrongest()
	 * @returns Returns first strongest cards among a given card range.
	 */
	
	public Card getStrongest(ArrayList<Card> cards){
		Card card = null;
		if(cards.size() == 1){
			card = cards.get(0);
			return card;
		}
		else{
			card = cards.get(0); //assume first card strongest
			for(int i = 1; i < cards.size(); i++){ //skip first card.
				//if another card has greater strengh (force), set strongest card to that card.
				if(card.getForce().retrieve() < cards.get(i).getForce().retrieve()){
					card = cards.get(i);
				}
			}
		}
		
		return card;
	}
	
	/**
	 * 	getStrongestAndRemove()
	 * @return Returns first strongest card in current hand and removes it from it.
	 */
	public Card getStrongestAndRemove(){
		if(currentCards.size() == 1){
			Card dummy = currentCards.get(0);
			currentCards.remove(0);
			return dummy;
		}
		else if(currentCards != null && currentCards.size() > 0){
			Card dummyCard = currentCards.get(0);
			for(int index = 1; index < currentCards.size(); index++){
				if(dummyCard.getForce().retrieve() < currentCards.get(index).getForce().retrieve()){
					dummyCard = currentCards.get(index);
					currentCards.remove(index);
				}
			}
			return dummyCard;
		}
		return new Card();
	}
	
	/***********************************************************
	 * SORTING METHODS
	 ***********************************************************/
	
	//CHANGE CAREFULLY
	/**
	 * 	sortAscending
	 * @goal Sorts by ascending order cards in current hand.
	 * 		 If two or more cards have the same force, it will sort
	 * 		 in the following symbol order: CLUBS, DIAMONDS, HEARTS, SPADES.
	 */
	public void sortAscending(){
		if(currentCards != null && currentCards.size() > 0){
			boolean done = false;
			ArrayList<Card> temp = new ArrayList<Card>();
			while(!done){
				if(currentCards.isEmpty()){
					done = true;
				}
				else{
					if(!(currentCards.isEmpty())){
						temp.add(getWeakest()); //add weakest card to temp list
						removeCard(getWeakest()); //remove it from the current hand.
					}
				}
			}
			currentCards = temp;
		}
	}
	
	//CHANGE CAREFULLY, HAND DETECTION METHOD IS HIGHLY DEPENDANT ON THIS METHOD
	/**
	 * 	sortDescending
	 * @goal Sorts by descending order cards in current hand.
	 * 		 If two or more cards have the same force, it will sort
	 * 		 in the following symbol order: SPADES, HEARTS, DIAMONDS, CLUBS.
	 */
	public void sortDescending(){
		if(currentCards != null && currentCards.size() > 0){
			boolean done = false;
			ArrayList<Card> temp = new ArrayList<Card>();
			while(!done){
				if(currentCards.isEmpty()){
					done = true;
				}
				else{
					if(!currentCards.isEmpty()){
						temp.add(getStrongest()); //add weakest card to temp list
						removeCard(getStrongest()); //remove it from the current hand.
					}
				}
			}
			currentCards = new ArrayList<Card>();
			currentCards.addAll(temp);
		}
	}
	
	/**
	 * 			equals
	 * @param hand to compare to.
	 * @return True if the both hands' have the same number of cards with the same strength.
	 * 		   Null if both hands don't have the same number of cards or any of the hands are invalid.
	 */
	public boolean equals(Hand hand){
		boolean isEqual = false;
		if(hand.getCards().size() != currentCards.size() || !(hand.hasValidCards() || hasValidCards())){
			return false;
		}
		sortDescending();
		hand.sortDescending();
		for(int i = 0; i < currentCards.size() ; i++){
			if(currentCards.get(i).getForce().retrieve() == hand.getCards().get(i).getForce().retrieve()){
				isEqual = true;
			}
			else{
				isEqual = false;
				break;
			}
		}
		return isEqual;
	}
	
	/*************************************************
	 * Generic Utility METHODS
	 *************************************************/
	
	public ArrayList<Card> getCards(){
		return currentCards;
	}
	
	public String toString(){
		String toString = "";
		for(int i = 0; i < currentCards.size(); i++){
			toString = toString + currentCards.get(i).toString() + " ";
		}
		return toString;
	}

	public AbstractHandConfiguration getCurrentHandConf() {
		return currentHandConf;
	}

	public void setCurrentHandConf(AbstractHandConfiguration currentHandConf) {
		this.currentHandConf = currentHandConf;
	}
	
	public ArrayList<Card> getStrongestCardsFromConfig(){
		ArrayList<Card> cards2Return = new ArrayList<Card>();
		if(currentHandConf != null){
			sortDescending();
			switch(currentHandConf.getRank()){
				case 0: //return highest card;
						cards2Return.add(currentCards.get(0));
						break;
				case 1: // return pair;
						boolean breakLoop = false;
						for(int i = 0; i < currentCards.size() && !breakLoop; i++){
							for(int j = 0; j < currentCards.size() && !breakLoop; j++){
								if(j != i){
									if(currentCards.get(i).equals(currentCards.get(j))){
										cards2Return.add(currentCards.get(j));
										cards2Return.add(currentCards.get(i));
										breakLoop = true;
										break;
									}
								}
							}			
						}
						break;
						
				case 2: // return the first two pairs;
						breakLoop = false;
						for(int i = 0; i < currentCards.size() && !breakLoop; i++){
							for(int j = 0; j < currentCards.size() && !breakLoop; j++){
								if(j != i){
									if(currentCards.get(i).equals(currentCards.get(j))){
										cards2Return.add(currentCards.get(j));
										cards2Return.add(currentCards.get(i));
										breakLoop = true;
										break;
									}
								}
							}			
						}
						breakLoop = false;
						for(int i = 0; i < currentCards.size() && !breakLoop; i++){
							for(int j = 0; j < currentCards.size() && !breakLoop; j++){
								if(j != i){
									if(currentCards.get(i).equals(currentCards.get(j))){
										cards2Return.add(currentCards.get(j));
										cards2Return.add(currentCards.get(i));
										breakLoop = true;
										break;
									}
								}
							}			
						}
						break;
				case 3: // return the first three of a kind
						breakLoop = false;
						for(int i = 0; i < currentCards.size() && !breakLoop; i++){
							for(int j = 0; j < currentCards.size() && !breakLoop; j++){
								if(j != i){
									for(int k = 0; k < currentCards.size(); k++){
										if(k != j && k!= i){
											if(currentCards.get(i).equals(currentCards.get(j))){
												if(currentCards.get(j).equals(currentCards.get(k))){
													cards2Return.add(currentCards.get(i));
													cards2Return.add(currentCards.get(j));
													cards2Return.add(currentCards.get(k));
													breakLoop = true;
													break;
												}
											}
											
										}
									}
								}
							}			
						}
						break;
				case 4: // return straight
				case 5: //return flush
						cards2Return.addAll(currentCards);
						break;
				case 6: // return full house
						breakLoop = false;
						int k = 0;
						for(int i = 0; i < currentCards.size() && !breakLoop; i++){
							for(int j = 0; j < currentCards.size() && !breakLoop; j++){
								if(j != i){
									for(k = 0; k < currentCards.size(); k++){
										if(k != j && k!= i){
											if(currentCards.get(i).equals(currentCards.get(j))){
												if(currentCards.get(j).equals(currentCards.get(k))){
													cards2Return.add(currentCards.get(i));
													cards2Return.add(currentCards.get(j));
													cards2Return.add(currentCards.get(k));
													breakLoop = true;
													break;
												}
											}
											
										}
									}
								}
							}			
						}
						for(int t = 0; t < currentCards.size(); t++){
							if(!(currentCards.get(k).equals(currentCards.get(t)))){
								cards2Return.add(currentCards.get(t));
							}
						}
						break;
				case 7: //return four of a kind
						breakLoop = false;
						for(int i = 0; i < currentCards.size() && !breakLoop; i++){
							for(int j = 0; j < currentCards.size() && !breakLoop; j++){
								if(j != i){
									for(k = 0; k < currentCards.size(); k++){
										if(k != j && k!= i){
											if(currentCards.get(i).equals(currentCards.get(j))){
												if(currentCards.get(j).equals(currentCards.get(k))){
													for(int t = 0; t < currentCards.size() && !breakLoop; t++){
														if(t != k && t != j && t != i){
															if(currentCards.get(t).equals(currentCards.get(k))){
																cards2Return.add(currentCards.get(i));
																cards2Return.add(currentCards.get(j));
																cards2Return.add(currentCards.get(k));
																cards2Return.add(currentCards.get(t));
																breakLoop = true;
																break;
															}
														}
													}
												}
											}
											
										}
									}
								}
							}			
						}
						break;
				case 8: //straight flush
				case 9: //royal flush
						cards2Return.addAll(currentCards);
						break;
				default:cards2Return.addAll(currentCards);
			}
		}
		return cards2Return;
	}
	
	public Hand clone(){
		ArrayList<Card> arr = new ArrayList<Card>();
		arr.addAll(currentCards);
		return new Hand(arr);
	}
	
	/**
	 * 		hasValidCards()
	 * @return true if all cards return true through Card.isValid method.
	 */
	public boolean hasValidCards(){
		boolean valid = false;
		for(Card cards : currentCards){
			if(cards.isValid()){
				valid = true;
			}
			else{
				valid = false;
				break;
			}
		}
		return valid;
	}

}
